﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;

namespace mal.WP7.Framework.Controls
{
    public partial class BarChart : UserControl
    {
        #region Fields
        public static readonly DependencyProperty ValuesProperty
        = DependencyProperty.Register("Values", typeof(Dictionary<object, double>),
                                  typeof(BarChart),
                                  new PropertyMetadata(null, ValuesChanged));

        public static readonly DependencyProperty FillProperty
        = DependencyProperty.Register("Fill", typeof(Brush),
                                 typeof(BarChart),
                                 new PropertyMetadata(new SolidColorBrush(Colors.Yellow), FillChanged));

        public static readonly DependencyProperty HighlightFillProperty
        = DependencyProperty.Register("Fill", typeof(Brush),
                       typeof(BarChart),
                       new PropertyMetadata(null, HighlightFillChanged));

        public static readonly DependencyProperty YAxisTitleProperty
         = DependencyProperty.Register(
                                   "YAxisTitle", typeof(string),
                                   typeof(BarChart),
                                   new PropertyMetadata(null, YAxisTitleChanged));

        private double maxValue;
        private int nbBars;
        private double graphWidth;
        private double graphHeight;
        private double barWidth;

        private Brush lineBrush = new SolidColorBrush(Colors.White);
        #endregion

        #region Construction / Destruction
        public BarChart()
        {
            InitializeComponent();
            popupValue.Visibility = System.Windows.Visibility.Collapsed;
        }

        #endregion

        #region enum, struct, class

        #endregion

        #region Properties
        public Dictionary<object, double> Values
        {
            get
            {
                return (Dictionary<object, double>)GetValue(ValuesProperty);
            }
            set
            {
                SetValue(ValuesProperty, value);
            }
        }

        public Brush Fill
        {
            get
            {
                return (Brush)GetValue(FillProperty);
            }
            set
            {
                try
                {
                    SetValue(FillProperty, value);
                }
                catch (Exception exp)
                {
                    System.Diagnostics.Debug.WriteLine(exp.Message);
                }
            }
        }

        public Brush HighlightFill
        {
            get
            {
                return (Brush)GetValue(HighlightFillProperty);
            }
            set
            {
                SetValue(HighlightFillProperty, value);
            }
        }

        public string YAxisTitle
        {
            get
            {
                return (string)GetValue(YAxisTitleProperty);
            }
            set
            {
                SetValue(YAxisTitleProperty, value);
            }
        }
        #endregion

        #region Methods
        private static void FillChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((BarChart)d).Fill = (Brush)e.NewValue;
        }

        private static void HighlightFillChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((BarChart)d).HighlightFill = (Brush)e.NewValue;
        }

        private static void YAxisTitleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((BarChart)d).YAxisTitle = (string)e.NewValue;
        }

        private static void ValuesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((BarChart)d).SetValues((Dictionary<object, double>)e.NewValue);
        }

        private void SetValues(Dictionary<object, double> value)
        {
            this.Cleanup();

            this.Values = value;
            this.maxValue = this.Values.Values.Max();
            this.nbBars = this.Values.Count;
            this.graphWidth = this.LayoutRoot.ActualWidth;
            this.graphHeight = this.LayoutRoot.ActualHeight;

            this.barWidth = this.graphWidth / this.nbBars;

            this.DrawYAxis();
            this.DrawBars();
        }

        private void Cleanup()
        {
            YAxis.Children.Clear();
            XAxis.Children.Clear();
            LayoutRoot.Children.Clear();
        }

        private void DrawYAxis()
        {
            int yaxisStep = 1;
            if (this.maxValue > 10)
            {
                yaxisStep = (int)this.maxValue / 10;
            }

            yaxisStep = int.Parse(yaxisStep.ToString().Substring(0, 1).PadRight(yaxisStep.ToString().Length, '0'));

            int value = yaxisStep;

            while (value < this.maxValue)
            {
                double top = this.graphHeight - (value * this.graphHeight / this.maxValue);

                TextBlock t = new TextBlock();
                t.Text = value.ToString();
                Canvas.SetTop(t, top - 20);
                Canvas.SetLeft(t, 0);
                YAxis.Children.Add(t);

                value += yaxisStep;

                this.DrawHorizontalLines(top);
            }
        }

        private void DrawHorizontalLines(double top)
        {
            Rectangle line = new Rectangle();
            double width = 15;

            line.Fill = lineBrush;
            line.Height = 1;
            line.Width = LayoutRoot.ActualWidth + width;

            Canvas.SetTop(line, top);
            Canvas.SetLeft(line, -width);

            LayoutRoot.Children.Add(line);
        }

        private void DrawVerticalLines(double left)
        {
            Rectangle line = new Rectangle();
            double width = 15;

            line.Fill = lineBrush;
            line.Height = LayoutRoot.ActualHeight + width;
            line.Width = 1;

            Canvas.SetTop(line, 0);
            Canvas.SetLeft(line, left);

            LayoutRoot.Children.Add(line);
        }

        private void DrawBars()
        {
            for (int i = 0; i < this.nbBars; i++)
            {
                // All most crashes during certification seems to appear on line Canvas.SetTop.
                // I don't know why. I hope this try/catch fix the bug.
                try
                {
                    double item = this.Values.Values.ToList()[i];
                    BarChartItem plot = new BarChartItem();
                    plot.Width = this.barWidth;
                    plot.Height = item * this.graphHeight / this.maxValue;
                    plot.Fill = this.Fill;
                    plot.HighlightFill = this.HighlightFill;
                    plot.Tag = i;

                    plot.MouseEnter += new System.Windows.Input.MouseEventHandler(plot_MouseEnter);
                    plot.MouseLeave += new System.Windows.Input.MouseEventHandler(plot_MouseLeave);
                    Canvas.SetTop(plot, this.graphHeight - plot.Height);
                    Canvas.SetLeft(plot, i * this.barWidth);
                    LayoutRoot.Children.Add(plot);

                    TextBlock xLabel = new TextBlock();
                    xLabel.Text = this.Values.Keys.ToList()[i].ToString();

                    if ((i % 5) == 0)
                    {
                        Canvas.SetTop(xLabel, 20 * (i % 2));
                        Canvas.SetLeft(xLabel, i * this.barWidth);
                        XAxis.Children.Add(xLabel);

                        this.DrawVerticalLines(i * this.barWidth);
                    }

                    plot.Start();
                }
                catch (Exception exp)
                {
                    System.Diagnostics.Debug.WriteLine(exp.Message);
                }
            }
        }
        #endregion

        #region Events
        private void plot_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
        {
            // this.popupValue.Visibility = Visibility.Collapsed;
        }

        private void plot_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
        {

        }
        #endregion
    }
}
